﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Net;
using System.IO;

namespace WeFeTeLib
{
    /// <summary>
    /// Test a URL given against the Framework passed.
    /// </summary>
    public class FrameworkTester
    {
        public event OnTestFinishedDelegated OnTestFinished;
        public delegate void OnTestFinishedDelegated(string name, int percent, List<Result> framework);
        public event OnPartCompletedDelegated OnPartCompleted;
        public delegate void OnPartCompletedDelegated(int amount);

        string url;
        Framework framework;

        event Framework.FileHandler fileHandler;
        event Framework.CookieHandler cookieHandler;
        event Framework.FolderHandler folderHandler;
        event Framework.HeaderHandler headerHandler;

        /// <summary>
        /// Generate a new FrameworkTester to test a framework
        /// </summary>
        /// <param name="URL">The URL to test (with / at the end)</param>
        /// <param name="frameworkType">The name of the Framework</param>
        public FrameworkTester(string URL, FrameworkFactory.FrameworkType frameworkType)
        {
            this.url = URL;

            framework = FrameworkFactory.CreateTest(frameworkType);

            FileListener fileListener = new FileListener();
            fileHandler += new Framework.FileHandler(fileListener.SaveResult);
            
            CookieListener cookieListener = new CookieListener();
            cookieHandler += new Framework.CookieHandler(cookieListener.SaveResult);

            FolderListener folderListener = new FolderListener();
            folderHandler += new Framework.FolderHandler(folderListener.SaveResult);

            HeaderListener headerListener = new HeaderListener();
            headerHandler += new Framework.HeaderHandler(headerListener.SaveResult);
        }

        /// <summary>
        /// Begins the test against the server
        /// </summary>
        public void Start()
        {
            try
            {
                WebClient wc = new WebClient();
                string s = wc.DownloadString(url);
            }
            catch (Exception e)
            {
                OnTestFinished("ERROR", 0, framework.getDebugResults());
                return;
            }

            TestFiles(framework);
            OnPartCompleted(((framework.frameworkFiles.Count * 100) / framework.totalTests));
            TestCookies(framework);
            OnPartCompleted(((framework.frameworkCookies.Count * 100) / framework.totalTests));
            TestFolders(framework);
            OnPartCompleted(((framework.frameworkFolders.Count * 100) / framework.totalTests));
            TestHeaders(framework);
            OnPartCompleted(((framework.frameworkHeaders.Count * 100) / framework.totalTests));
            
            OnTestFinished(framework.Name, framework.getAccuracy(), framework.getDebugResults());
        }


        /// <summary>
        /// Test the File objects
        /// </summary>
        /// <param name="framework">The Framework object to test.</param>
        private void TestFiles(Framework framework)
        {
            List<File> files = framework.getDefaultFiles();
            
            foreach (File file in files)
            {
                int numCheck = 0;
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url + file.Name);
                request.AllowAutoRedirect = false; // Disable 301 and 302 codes

                try
                {
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                    if (file.HTTPCode == response.StatusCode)
                    {
                        if (!String.IsNullOrEmpty(file.Content))
                        {
                            StreamReader strReader = new StreamReader(response.GetResponseStream());
                            string r = strReader.ReadToEnd();
                            if (r.Contains(file.Content))
                            {
                                numCheck++;
                            }
                        }
                        else
                        {
                            if (file.FileLength == response.ContentLength)
                            {
                                numCheck++;
                            }
                        }
                    }

                    fileHandler(framework, new FileEventArgs(file, numCheck));
                    response.Close();
                }
                catch (WebException e)
                {
                    if (e.Response == null)
                        break;

                    if (file.HTTPCode == ((HttpWebResponse)e.Response).StatusCode)
                    {
                        if (!String.IsNullOrEmpty(file.Content))
                        {
                            StreamReader strReader = new StreamReader(e.Response.GetResponseStream());
                            string r = strReader.ReadToEnd();
                            if (r.Contains(file.Content))
                            {
                                numCheck++;
                            }
                        }
                        else
                        {
                            if (file.FileLength == e.Response.ContentLength)
                            {
                                numCheck++;
                            }
                        }
                    }

                    fileHandler(framework, new FileEventArgs(file, numCheck));

                    if (e.Response != null)
                        e.Response.Close();
                }
            }
        }

        private void TestCookies(Framework framework)
        {
            List<Cookie> cookies = framework.getDefaultCookies();

            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            request.AllowAutoRedirect = false; // Disable 301 and 302 codes

            try
            {
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                string responseSetCookies = response.Headers[HttpResponseHeader.SetCookie];

                List<Cookie> responseCookies = ExtractCookies(responseSetCookies);

                // Remove duplicated cookies
                try
                {
                    responseCookies.Sort();
                    int index = 0;
                    while (index < responseCookies.Count - 1)
                    {
                        if (responseCookies[index] == responseCookies[index + 1])
                            responseCookies.RemoveAt(index);
                        else
                            index++;
                    }
                }
                catch (InvalidOperationException e) { }

                foreach (Cookie cf in cookies)
                {
                    foreach (Cookie cr in responseCookies)
                    {
                        int numCheck = 0;
                        if (cf.ValueLength == cr.ValueLength)
                            numCheck++;
                        if (cr.Name.Contains(cf.Name))
                            numCheck++;

                        if (numCheck >= 1)
                            cookieHandler(framework, new CookieEventArgs(cf, numCheck));
                    }
                }

                response.Close();
            }
            catch (WebException e)
            {
                //cookieHandler(framework, new CookieEventArgs(cookieFramework, 0));
                if (e.Response != null)
                    e.Response.Close();
            }
        }

        private void TestFolders(Framework framework)
        {
            List<Folder> folders = framework.getDefaultFolders();


            foreach (Folder folder in folders)
            {
                int numCheck = 0;
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url + folder.Name);
                request.AllowAutoRedirect = false; // Disable 301 and 302 codes

                try
                {
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                    if (folder.HTTPStatus == response.StatusCode)
                        numCheck++;

                    folderHandler(framework, new FolderEventArgs(folder, numCheck));
                    response.Close();
                }
                catch (WebException e)
                {
                    if (e.Response == null)
                        break;

                    if ((folder.HTTPStatus == ((HttpWebResponse)e.Response).StatusCode))
                        numCheck++;

                    folderHandler(framework, new FolderEventArgs(folder, numCheck));

                    if (e.Response != null)
                        e.Response.Close();
                }

            }
        }

        private void TestHeaders(Framework framework)
        {
            List<Header> frameworkHeaders = framework.getDefaultHeaders();

            foreach (Header header in frameworkHeaders)
            {
                int numCheck = 0;

                try
                {
                    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url + header.HeaderFile);
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                    if (response.Headers[header.HeaderName] != null)
                    {
                        if (response.Headers[header.HeaderName].Contains(header.HeaderValue))
                        {
                            numCheck++;
                        }
                    }

                    headerHandler(framework, new HeaderEventArgs(header, numCheck));

                    response.Close();
                }
                catch (WebException e)
                {
                    HttpWebResponse response = (HttpWebResponse)e.Response;

                    if (response.Headers[header.HeaderName] != null)
                    {
                        if (response.Headers[header.HeaderName].Contains(header.HeaderValue))
                        {
                            numCheck++;
                        }
                    }

                    headerHandler(framework, new HeaderEventArgs(header, numCheck));

                    if(response != null)
                        response.Close();
                }
            }
        }

        /// <summary>
        /// Extract cookies into a List of Cookies objects
        /// </summary>
        /// <param name="responseSetCookies">The Set-Cookie header raw response</param>
        /// <returns></returns>
        private List<Cookie> ExtractCookies(string responseSetCookies)
        {
            if (responseSetCookies == null)
                return new List<Cookie>();

            List<Cookie> result = new List<Cookie>();
            string[] raw = responseSetCookies.Split(',');
            string[] cookieData = new string[raw.Length];

            // Where the magic begins...
            for (int i = 0; i < raw.Length; i++)
            {
                string[] tmp = raw[i].Split(';');
                cookieData[i] = tmp[0];
            }

            foreach (string s in cookieData)
            {
                try
                {
                    string name = s.Substring(0, s.IndexOf('='));
                    string value = s.Substring(s.IndexOf('=') + 1, s.Length - s.IndexOf('=') - 1);
                    result.Add(new Cookie(name, value.Length));
                }
                catch (Exception e)
                {
                    // Only malformed cookies here...
                }
            }

            return result;
        }


    }
}